package me.leiusual.sharing.repository

import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations.switchMap
import androidx.paging.Config
import androidx.paging.PagedList
import androidx.paging.toLiveData
import io.reactivex.Single
import me.leiusual.sharing.api.ApiOauth2
import me.leiusual.sharing.api.ApiSharing
import me.leiusual.sharing.api.model.*
import me.leiusual.sharing.api.model.usertotal.UserTotal
import me.leiusual.sharing.pref.Store
import me.leiusual.sharing.pref.Token
import me.leiusual.sharing.pref.User
import me.leiusual.sharing.repository.datasource.UserInvitedDataSourceFactory
import me.leiusual.sharing.repository.exception.MobileRegexException
import me.leiusual.sharing.repository.exception.PasswordRegexException
import me.leiusual.sharing.repository.exception.PasswordUpdateException
import me.leiusual.sharing.repository.model.Data
import me.leiusual.sharing.ui.account.model.ParamsAccessToken
import me.leiusual.sharing.ui.account.model.ParamsPasswordReset
import me.leiusual.sharing.ui.account.model.ParamsRegister
import me.leiusual.sharing.ui.account.model.ParamsRegisterByQRCode
import me.leiusual.sharing.ui.mine.model.ParamsMobileUpdate
import me.leiusual.sharing.ui.mine.model.ParamsPasswordUpdate
import me.leiusual.sharing.ui.mine.model.ParamsRealName
import me.leiusual.sharing.util.extension.compressIdCard
import me.leiusual.sharing.util.extension.matchesMobile
import me.leiusual.sharing.util.extension.matchesPassword
import okhttp3.MediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Create by leiusual on 2018/7/18
 */
@Singleton
class UserRepository @Inject constructor(
    private val token: Token,
    private val user: User,
    private val apiOauth2: ApiOauth2,
    private val apiSharing: ApiSharing,
    private val store: Store
) {

  fun accessToken(paramsAccessToken: ParamsAccessToken): Single<UserInfo.Response> {
    return Single
        .fromCallable {
          if (paramsAccessToken.username.isBlank()) {
            throw Exception("账号不能为空")
          }
          if (paramsAccessToken.password.isBlank()) {
            throw Exception("密码不能为空")
          }
        }
        .flatMap {
          apiOauth2.accessToken(paramsAccessToken.username, paramsAccessToken.password)
        }
        .doOnSuccess {
          it.replace(token)
        }
        .flatMap {
          userInfo()
        }
  }

  fun userInfo(): Single<UserInfo.Response> {
    return apiOauth2.userInfo()
        .doOnSuccess {
          it.replace(user)
        }
  }

  fun sendSMS(type: SendSMS.Type, phone: String): Single<Unit> {
    return apiOauth2.sendSMS(SendSMS.Request(ApiOauth2.CLIENT_ID, type, phone, user.userId))
  }

  fun passwordUpdate(paramsPasswordUpdate: ParamsPasswordUpdate): Single<Unit> {
    return Single
        .fromCallable {

          if (!paramsPasswordUpdate.passwordNew.matchesPassword()) {
            throw PasswordRegexException()
          }
          if (paramsPasswordUpdate.passwordNew != paramsPasswordUpdate.passwordTwo) {
            throw PasswordUpdateException("确认密码不一致")
          }

          PasswordUpdate.Request(
              ApiOauth2.CLIENT_ID,
              paramsPasswordUpdate.passwordTwo,
              paramsPasswordUpdate.passwordOld
          )
        }
        .flatMap {
          apiOauth2.passwordUpdate(it)
        }
  }

  fun register(paramsRegister: ParamsRegister): Single<Register.Response> {
    return Single
        .fromCallable {

          if (!paramsRegister.mobile.matchesMobile()) {
            throw MobileRegexException()
          }

          Register.Request(
              paramsRegister.checkCode,
              ApiOauth2.CLIENT_ID,
              paramsRegister.inviteCode,
              paramsRegister.mobile
          )
        }
        .flatMap {
          apiOauth2.register(it)
        }
  }

  fun invited(currentUserId: Long, targetUserId: Long): InvitedListing {
    val factory = UserInvitedDataSourceFactory(apiSharing, currentUserId, targetUserId)
    val loadInitial = switchMap(factory.userInvitedDataSource) { it.loadInitial }
    val pagedList = factory.toLiveData(Config(9, initialLoadSizeHint = 9))
    return InvitedListing(loadInitial, pagedList)
  }

  data class InvitedListing(
      val loadInitial: LiveData<Data<Invited.Response.User>>,
      val pagedList: LiveData<PagedList<Invited.Response.User>>
  )

  fun inviteQRCode(): Single<InviteQRCode.Response> {
    return apiSharing.inviteQRCode(InviteQRCode.Request(ApiOauth2.CLIENT_ID))
  }

  fun realName(paramsRealName: ParamsRealName): Single<Unit> {
    return Single
        .fromCallable {

          val front = paramsRealName.front ?: throw Exception("无身份证正面照")
          val back = paramsRealName.back ?: throw Exception("无身份证背面照")

          val frontFile = (File(front).compressIdCard() ?: throw Exception("图片压缩失败"))
          val backFile = (File(back).compressIdCard() ?: throw Exception("图片压缩失败"))

          val frontBody = RequestBody.create(MediaType.parse("image/png"), frontFile)
          val backBody = RequestBody.create(MediaType.parse("image/png"), backFile)

          MultipartBody.Builder()
              .setType(MultipartBody.FORM)
              .addFormDataPart("frontFile", "frontFile.png", frontBody)
              .addFormDataPart("backFile", "backFile.png", backBody)
              .addFormDataPart("name", paramsRealName.realName)
              .addFormDataPart("cardType", paramsRealName.cardType.name)
              .addFormDataPart("cardNo", paramsRealName.idCard)
              .addFormDataPart("clientId", ApiOauth2.CLIENT_ID)
              .addFormDataPart("checkCode", paramsRealName.checkCode)
              .build()
        }
        .flatMap {
          apiOauth2.realName(it)
        }
  }

  fun realNameQuery(): Single<RealNameQuery.Response> {
    return apiOauth2.realNameQuery(RealNameQuery.Request(ApiOauth2.CLIENT_ID))
  }

  fun mobileUpdate(paramsMobileUpdate: ParamsMobileUpdate): Single<Unit> {
    return Single
        .fromCallable {

          if (!paramsMobileUpdate.mobile.matchesMobile()) {
            throw MobileRegexException()
          }

          MobileUpdate.Request(
              paramsMobileUpdate.checkCode,
              ApiOauth2.CLIENT_ID,
              paramsMobileUpdate.mobile,
              if (paramsMobileUpdate.verifyCode.isEmpty()) null else paramsMobileUpdate.verifyCode
          )
        }
        .flatMap {
          apiOauth2.mobileUpdate(it)
        }
  }

  fun registerByQRCode(params: ParamsRegisterByQRCode): Single<RegisterByQRCode.Response> {
    return apiOauth2.registerByQRCode(RegisterByQRCode.Request(params.encodedClientId, params.invitedCode))
  }

  fun passwordReset(params: ParamsPasswordReset): Single<Unit> {
    return Single
        .fromCallable {

          if (!params.mobile.matchesMobile()) {
            throw MobileRegexException()
          }
          if (!params.newPassword.matchesPassword()) {
            throw PasswordRegexException()
          }
          if (params.checkCode.isBlank()) {
            throw Exception("验证码不能为空")
          }

          PasswordReset.Request(
              params.checkCode,
              ApiOauth2.CLIENT_ID,
              params.mobile,
              params.username,
              params.newPassword
          )
        }
        .flatMap {
          apiOauth2.passwordReset(it)
        }
  }

  fun avatarUpdate(filePath: String): Single<String> {
    return Single
        .fromCallable {
          val file = (File(filePath).compressIdCard() ?: throw Exception("图片压缩失败"))
          val fileBody = RequestBody.create(MediaType.parse("image/png"), file)
          MultipartBody.Builder()
              .setType(MultipartBody.FORM)
              .addFormDataPart("newAvatar", "newAvatar.png", fileBody)
              .addFormDataPart("clientId", ApiOauth2.CLIENT_ID)
              .build()
        }
        .flatMap {
          apiOauth2.avatarUpdate(it)
        }
        .map {
          it.get("data").asString
        }
  }

  fun nicknameUpdate(nickname: String): Single<Unit> {
    return apiOauth2.nicknameUpdate(NicknameUpdate.Request(ApiOauth2.CLIENT_ID, nickname))
  }

  fun userTotal(): Single<UserTotal> {
    return apiSharing.userTotal()
        .doOnSuccess {
          user.userIdInClient = it.userId
        }
  }

  fun userStoreList(): Single<Map<Long, String>> {
    return Single
        .fromCallable {
          store.managedStoreList
        }
  }
}